package com.douyinkan.service.impl;

import com.douyinkan.entity.Content;
import com.douyinkan.mapper.ContentMapper;
import com.douyinkan.service.DatabaseService;
import com.douyinkan.service.DataSourceManagerService;
import com.douyinkan.service.DirectoryScanService;
import com.douyinkan.util.FileUtil;
import com.douyinkan.util.MediaUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class DirectoryScanServiceImpl implements DirectoryScanService {

    private static final Logger logger = LoggerFactory.getLogger(DirectoryScanServiceImpl.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private DataSourceManagerService dataSourceManagerService;

    @Autowired
    private DatabaseService databaseService;

    @Autowired
    private ContentMapper contentMapper;

    private String currentMediaDir;

    public void setCurrentMediaDirectory(String mediaDir) {
        logger.info("=== Setting current media directory ===");
        logger.info("Setting current media directory to: {}", mediaDir);
        this.currentMediaDir = mediaDir;

        // 获取或创建对应媒体目录的数据源
        logger.info("Getting or creating data source for media directory: {}", mediaDir);
        dataSourceManagerService.getOrCreateDataSource(mediaDir);

        logger.info("=== Successfully set current media directory ===");
    }

    @Override
    public String getMediaMetaDir(String mediaDir) {
        logger.debug("Getting meta directory for media directory: {}", mediaDir);
        Path mediaPath = Paths.get(mediaDir);
        Path metaPath = mediaPath.resolve("myMediaMeta");

        // 创建隐藏目录
        try {
            if (!Files.exists(metaPath)) {
                logger.info("Creating meta directory: {}", metaPath);
                Files.createDirectories(metaPath);
                // 尝试将目录设置为隐藏（在不同操作系统中方式不同）
                makeHidden(metaPath.toFile());
                logger.info("Successfully created meta directory: {}", metaPath);
            } else {
                logger.debug("Meta directory already exists: {}", metaPath);
            }
        } catch (IOException e) {
            logger.error("Failed to create media meta directory: {}", metaPath, e);
        }

        String metaDir = metaPath.toString();
        logger.debug("Meta directory path: {}", metaDir);
        return metaDir;
    }

    @Override
    public List<Content> scanDirectory(String mediaDir) {
        logger.info("=== Starting scan directory ===");
        logger.info("Scanning directory: {}", mediaDir);

        // 如果媒体目录不同，则切换数据源
        if (!mediaDir.equals(currentMediaDir)) {
            logger.info("Media directory changed from {} to {}, switching data source", currentMediaDir, mediaDir);
            setCurrentMediaDirectory(mediaDir);
        } else {
            logger.debug("Current media directory is already set to: {}", mediaDir);
        }

        logger.info("Scanning directory: {}", mediaDir);

        String metaDir = getMediaMetaDir(mediaDir);
        Path mediaPath = Paths.get(mediaDir);

        List<Content> newContents = new ArrayList<>();

        try {
            logger.info("Starting to walk through directory: {}", mediaPath);
            AtomicLong fileCount = new AtomicLong(0); // 使用AtomicLong避免lambda表达式的final变量问题
            Files.walk(mediaPath)
                .filter(Files::isRegularFile)
                .filter(path -> {
                    boolean isValid = FileUtil.isValidMediaFile(path.toString());
                    if (isValid) {
                        long count = fileCount.incrementAndGet();
                        if (count % 10 == 0) { // 每10个文件记录一次，避免日志过多
                            logger.debug("Processing media file #{}: {}", count, path.toString());
                        }
                    }
                    return isValid;
                })
                .forEach(path -> {
                    String filePath = path.toString();
                    String fileName = path.getFileName().toString();

                    logger.debug("Processing file: {}", filePath);

                    // 检查数据库中是否已存在该文件
                    logger.debug("Checking if file already exists in DB: {}", filePath);
                    Content existingContent = findContentByFilePath(filePath);
                    if (existingContent != null) {
                        logger.debug("File already exists in DB, skipping: {}", filePath);
                        return; // 跳过已存在的文件
                    } else {
                        logger.debug("File does not exist in DB, will add: {}", filePath);
                    }

                    Content content = new Content();
                    content.setFilename(fileName);
                    content.setFilePath(filePath);
                    content.setContentType(FileUtil.getContentType(fileName));

                    File file = path.toFile();
                    content.setFileSizeBytes(file.length());
                    content.setCreatedAt(LocalDateTime.now());
                    content.setUpdatedAt(LocalDateTime.now());

                    // 为视频和图片生成缩略图
                    try {
                        String thumbnailPath = generateThumbnail(mediaDir, filePath, metaDir);
                        content.setThumbnailPath(thumbnailPath);
                        logger.debug("Generated thumbnail for: {} -> {}", filePath, thumbnailPath);

                        // 获取媒体文件的元数据（如果适用）
                        if ("video".equals(content.getContentType())) {
                            logger.debug("Extracting video metadata for: {}", filePath);
                            double duration = MediaUtil.getVideoDuration(filePath);
                            content.setDurationSeconds(duration);

                            int[] dimensions = MediaUtil.getVideoDimensions(filePath);
                            if (dimensions != null) {
                                content.setWidth(dimensions[0]);
                                content.setHeight(dimensions[1]);
                                logger.debug("Video dimensions: {}x{}", dimensions[0], dimensions[1]);
                            }
                        } else if ("image".equals(content.getContentType())) {
                            logger.debug("Extracting image metadata for: {}", filePath);
                            int[] dimensions = MediaUtil.getImageDimensions(filePath);
                            if (dimensions != null) {
                                content.setWidth(dimensions[0]);
                                content.setHeight(dimensions[1]);
                                logger.debug("Image dimensions: {}x{}", dimensions[0], dimensions[1]);
                            }
                        }
                    } catch (Exception e) {
                        logger.error("Error processing media file: {}", filePath, e);
                    }

                    content.setTitle(FileUtil.getFilenameWithoutExtension(fileName));
                    logger.debug("Added content to list: {}", content.getFilename());

                    newContents.add(content);
                });

            logger.info("Completed scanning directory: {}, found {} new media files", mediaDir, newContents.size());
        } catch (IOException e) {
            logger.error("Error scanning directory: {}", mediaDir, e);
        }

        logger.info("=== Scan directory completed ===");
        logger.info("Scan directory completed for: {}, total new files: {}", mediaDir, newContents.size());
        return newContents;
    }

    @Override
    public String generateThumbnail(String mediaDir, String filePath, String metaDir) {
        logger.debug("Generating thumbnail for file: {}", filePath);
        Path sourcePath = Paths.get(filePath);
        String fileName = sourcePath.getFileName().toString();
        String nameWithoutExt = FileUtil.getFilenameWithoutExtension(fileName);
        String extension = FileUtil.getFileExtension(fileName);

        // 使用元数据目录存储缩略图
        String thumbnailPath = Paths.get(metaDir, "thumbnails", nameWithoutExt + ".jpg").toString();

        try {
            // 确保缩略图目录存在
            Path thumbnailDir = Paths.get(metaDir, "thumbnails");
            if (!Files.exists(thumbnailDir)) {
                logger.info("Creating thumbnail directory: {}", thumbnailDir);
                Files.createDirectories(thumbnailDir);
            }

            if ("video".equals(FileUtil.getContentType(fileName))) {
                logger.info("Generating thumbnail for video: {}", filePath);
                // 为视频生成缩略图
                MediaUtil.generateVideoThumbnail(filePath, thumbnailPath);
            } else if ("image".equals(FileUtil.getContentType(fileName))) {
                logger.info("Generating thumbnail for image: {}", filePath);
                // 为图片生成缩略图
                MediaUtil.generateImageThumbnail(filePath, thumbnailPath);
            }

            logger.info("Successfully generated thumbnail for: {} -> {}", filePath, thumbnailPath);
            return thumbnailPath;
        } catch (Exception e) {
            logger.error("Error generating thumbnail for: {}", filePath, e);
            return null;
        }
    }

    private void makeHidden(File file) {
        try {
            // 在 Unix-like 系统上，以 . 开头的文件或目录是隐藏的
            if (file.getName().charAt(0) != '.') {
                // 已经是隐藏的（因为路径中包含 .myMediaMeta）
                Files.setAttribute(file.toPath(), "dos:hidden", true);
            }
        } catch (Exception e) {
            logger.debug("Could not set hidden attribute for file: {}", file.getAbsolutePath());
        }
    }

    // 由于ContentMapper依赖于当前数据源，我们需要确保在正确的时间点调用它
    private Content findContentByFilePath(String filePath) {
        logger.debug("Finding content by filepath: {}", filePath);
        try {
            Content result = contentMapper.findByFilePath(filePath);
            logger.debug("Find content by filepath result: {} for path: {}", result != null ? "FOUND" : "NOT FOUND", filePath);
            return result;
        } catch (Exception e) {
            logger.error("Database error finding content by filepath: {}", filePath, e);

            // 检查是否是 "no such table" 错误
            String exceptionMessage = e.getMessage();
            if (exceptionMessage != null && (exceptionMessage.contains("no such table") ||
                (e.getCause() != null && e.getCause().getMessage() != null &&
                 e.getCause().getMessage().contains("no such table")))) {

                logger.error("Table does not exist error for: {}", filePath);
                logger.error("Media directory: {}", currentMediaDir != null ? currentMediaDir : "NOT SET");

                // 尝试重新初始化数据库表结构
                if (currentMediaDir != null) {
                    try {
                        logger.info("Attempting to reinitialize database schema for: {}", currentMediaDir);
                        dataSourceManagerService.getOrCreateDataSource(currentMediaDir);
                        logger.info("Database schema reinitialization completed for: {}", currentMediaDir);
                    } catch (Exception initException) {
                        logger.error("Failed to reinitialize database schema: {}", initException.getMessage(), initException);
                    }
                }
            } else {
                logger.error("Other database error finding content by filepath: {}", filePath, e);
            }

            return null;
        }
    }
}